use std::io::Write;
use std::process::{Command, Stdio};

fn ext_from_lang(lang: &str) -> &'static str {
    match lang.to_lowercase().as_str() {
        "typescript" => "ts",
        "javascript" => "js",
        "json" => "json",
        "css" => "css",
        "html" => "html",
        "markdown" => "md",
        "rust" => "rs",
        _ => "txt",
    }
}

fn try_spawn(mut cmd: Command, input: &str) -> Result<Option<String>, String> {
    let child_res = cmd.stdin(Stdio::piped()).stdout(Stdio::piped()).spawn();
    if let Ok(mut child) = child_res {
        if let Some(mut stdin) = child.stdin.take() {
            let _ = stdin.write_all(input.as_bytes());
        }
        let out = child.wait_with_output().map_err(|e| e.to_string())?;
        if out.status.success() {
            return Ok(Some(String::from_utf8(out.stdout).map_err(|e| e.to_string())?));
        } else {
            // return Ok(None) to continue to next strategy
            return Ok(None);
        }
    }
    Ok(None)
}

#[tauri::command]
pub async fn format_code(language: String, content: String, path_hint: Option<String>) -> Result<String, String> {
    let filename = path_hint.unwrap_or_else(|| format!("file.{}", ext_from_lang(&language)));

    // 1) Try prettierd if available
    {
        // Windows may need .cmd, try both
        let candidates = if cfg!(target_os = "windows") {
            vec!["prettierd", "prettierd.cmd"]
        } else {
            vec!["prettierd"]
        };
        for bin in candidates {
            let mut cmd = Command::new(bin);
            cmd.arg("--stdin-filepath").arg(&filename);
            if let Ok(Some(res)) = try_spawn(cmd, &content) {
                return Ok(res);
            }
        }
    }

    // 2) Try dprint if available
    {
        let candidates = if cfg!(target_os = "windows") {
            vec!["dprint.exe", "dprint"]
        } else {
            vec!["dprint"]
        };
        for bin in candidates {
            let mut cmd = Command::new(bin);
            cmd.arg("fmt").arg("--stdin").arg(&filename);
            if let Ok(Some(res)) = try_spawn(cmd, &content) {
                return Ok(res);
            }
        }
    }

    // 3) Built-in JSON pretty
    if language.to_lowercase() == "json" {
        let v: serde_json::Value = serde_json::from_str(&content).map_err(|e| e.to_string())?;
        return serde_json::to_string_pretty(&v).map_err(|e| e.to_string());
    }

    // 4) Try rustfmt for rust
    if language.to_lowercase() == "rust" {
        let candidates = if cfg!(target_os = "windows") {
            vec!["rustfmt.exe", "rustfmt"]
        } else {
            vec!["rustfmt"]
        };
        for bin in candidates {
            let mut cmd = Command::new(bin);
            cmd.arg("--emit").arg("stdout").arg("--edition").arg("2021");
            if let Ok(Some(res)) = try_spawn(cmd, &content) {
                return Ok(res);
            }
        }
    }

    // 5) Fallback: return original content unchanged
    Ok(content)
}


